/**
 * 用户相关方法
 *
 * @format
 */
import { initBaseDao } from '../sdk';
import { USER_DB_NAME } from '../db';
import { initCustomUserWhenLogin, updateUserWhenLogin } from '../customUser'
import { getToday, formatUpdateUserProjection } from '../utils';
import { formatUpdatedDataByProjection } from '../utils/common/format'
import { isNewVip, isNewFollow, setNewVipUserData, setNewFollowUserData } from '../utils/common/userUpdate'
import { max, merge } from 'lodash'
import { RANK_VALUE_END, GAME_TYPE, INVITE_USER_TIME, MAX_RANK_VALUE, MAX_ERROR_TIME,MAX_ERROR_ADD_TIME, MAX_ERROR_RANK_VALUE } from '../constants'

export default class UserService {
  context: IContext<IParams>
  userdao: IBaseDao
  constructor(context: IContext<IParams>) {
    this.context = context;
    this.userdao = initBaseDao(context, USER_DB_NAME);
  }

  async doLogin(userInfo: IUserInfo) {
    if (!userInfo) {
      const customUserInfo = initCustomUserWhenLogin(this.context)
      userInfo = await this.initUserData({ ...customUserInfo })
    } else {
      userInfo = await this.updateUserData(userInfo)
    }
    return userInfo
  }

  /**
   * @desc 获取当前打开活动的用户详情
   * @returns 若用户不存在，返回null; 用户存在，返回用户信息(object对象)
   */
  async getUserInfo(projection = {}) {
    const { openId, data } = this.context
    const { pageId } = data
    return await this.userdao.findOne<IUserInfo>({ openId, pageId }, { projection })
  }

  /**
   * 初始化用户数据
   * @memberof UserService
   */
  async initUserData(customUserInfo: ICustomUserInfo) {
    const { openId, data } = this.context
    const { pageId, avatar, userNick, isVip = false, isFollow = false } = data
    const today = getToday()
    const user: ICommonUserInfo = {
      pageId,
      avatar,
      userNick,
      openId,
      login: {
        [today]: 1
      },
      member: {
        flag: isVip
      },
      follow: {
        flag: isFollow
      },
      createTime: Date.now(),
      createDay: today,
      updateTime: Date.now()
    }

    await this.userdao.insertOne<IUserInfo>({
      ...user,
      ...customUserInfo
    })

    return {
      ...user,
      ...customUserInfo
    }
  }

  /**
   * 更新用户信息
   * @param userInfo 
   * @returns 
   */
  async updateUserData(userInfo: IUserInfo): Promise<IUserInfo> {
    const { isVip = false, isFollow = false } = this.context.data
    const today = getToday()
    let projection: IUpdateQuery = {
      $set: {
        updateTime: Date.now()
      },
      $push: {},
      $inc: {
        [`login.${today}`]: 1
      }
    }

    // 之前进入活动非会员，现在进入会员，则视为新会员用户
    const newVip = isNewVip(userInfo, isVip)
    const newFollow = isNewFollow(userInfo, isFollow)
    if (newVip) {
      projection.$set.member = setNewVipUserData(userInfo.member)
    }
    if (newFollow) {
      projection.$set.follow = setNewFollowUserData(userInfo.follow)
    }

    projection = merge({}, projection, updateUserWhenLogin(this.context, userInfo))

    await this.updateUser(userInfo._id, projection)

    return formatUpdatedDataByProjection(userInfo, projection)
  }

  /**
   * @desc 更新用户表
   * @param {用户的主键id} _id
   * @param {更新的对象} document
   * @returns 若更新成功，返回为1； 若更新失败，返回为 0 或系统直接报错
   */
  async updateUser(_id: string, projection: IUpdateQuery) {
    return await this.userdao.update({ _id }, formatUpdateUserProjection(projection));
  }
  // 扣除用户凭证
  async reduceUserTimes(userInfo: IUserInfo, game: number, ticket?: number) {
    const { drawLotsTimes, sendBlessingTimes, todayDrawLotsTimes, todaySendBlessingTimes, openId, pageId } = userInfo;
    let reduceKey = ''
    // 摇签活动扣次数
    if(game === GAME_TYPE.DRAW_LOTS && (todayDrawLotsTimes + drawLotsTimes) > 0) {
      reduceKey = todayDrawLotsTimes > 0 ? 'todayDrawLotsTimes' : 'drawLotsTimes'
    }
    // 发送祝福扣次数
    if(game === GAME_TYPE.BLESSINGS && (sendBlessingTimes + todaySendBlessingTimes) > 0) {
      reduceKey = todaySendBlessingTimes > 0 ? 'todaySendBlessingTimes' : 'sendBlessingTimes'
    }
    if(!reduceKey) return false;
    const updateRes = await this.userdao.update({
      openId, pageId,
      $where: `this.${reduceKey} > 0`
    }, {
      $inc: { [reduceKey]: -(ticket || 1) },
      $set: { updateTime: Date.now() }
    })
    return updateRes
  }

  // 扣除游戏次数
  async deductGameTimes(userInfo: IUserInfo) {
    const game = +this.context.data.game
    const todayTimesKey = GAME_TYPE.DRAW_LOTS === game ? 'todayDrawLotsTimes' : 'todaySendBlessingTimes'
    const totalTimesKey = GAME_TYPE.DRAW_LOTS === game ? 'drawLotsTimes' : 'sendBlessingTimes'
    // 每次消耗的游戏次数
    const consumeGameTimes = 1
    return await this.userdao.update(
      {
        _id: userInfo._id,
        $where: `this.${totalTimesKey} + this.${todayTimesKey} >= ${consumeGameTimes}`
      },
      {
        $inc: {
          [todayTimesKey]:
            +consumeGameTimes > userInfo[todayTimesKey]
              ? -userInfo[todayTimesKey]
              : -consumeGameTimes,
          [totalTimesKey]:
            +consumeGameTimes > userInfo[todayTimesKey]
              ? userInfo[todayTimesKey] - consumeGameTimes
              : 0,
        },
        $set: {
          updateTime: Date.now(),
        }
      }
    )
  }

  // 更新用户福气值
  async updateLuckPoint(userInfo: IUserInfo, point: number) {
    const { rankLuckPoint, hasAddTimeUser = false } = userInfo;
    // 判断是否已经超过排行榜排名时间
    const hasRankValue = (Date.now() <= RANK_VALUE_END) && (rankLuckPoint + point) <= MAX_RANK_VALUE
    // 如果30号24点前已达到
    const hasAddTime = Date.now() < MAX_ERROR_TIME && (rankLuckPoint + point) > MAX_ERROR_RANK_VALUE
    // 排行榜完成时间
    const setUpdateRankTime = hasAddTimeUser ? { updateTime: Date.now() } : { 
      updateRankTime: hasAddTime ? Date.now() + MAX_ERROR_ADD_TIME : Date.now(),
      hasAddTimeUser: hasAddTime // 给加时间的用户打个标
    }
    const res = await this.userdao.update({ _id: userInfo._id }, {
      $inc: { luckPoint: point, rankLuckPoint: hasRankValue ? point : 0 },
      $set: hasRankValue ? setUpdateRankTime : { updateTotalTime: Date.now() }
    })
    return res;
  }

  // 获取用户排名
  async getUserRank(openId: string, pageId: number, rankValue: number) {
    if (!rankValue) return '未参与'
    // 获取大于自己福气值的用户数
    const maxNum = await this.userdao.count({ pageId, rankLuckPoint: { $gt: rankValue } })
    // 获取和自己相同分数的用户
    const sameNumList = await this.userdao.aggregate([
      { $match: { pageId, rankLuckPoint: rankValue } },
      { $sort: { updateRankTime: 1 }},
      { $project: { openId: 1, updateRankTime: 1 } }
    ])
    const sameNum = sameNumList.findIndex(k => k.openId === openId)
    return maxNum + (sameNum < 0 ? 0 : sameNum) + 1
  }

  // 更新任务次数和发放游戏奖励
  async updateUserTaskTimes (user: IUserInfo, times: number)  {
    const { type, sellerId } = this.context.data
    const game = +this.context.data.game
    const key = GAME_TYPE.DRAW_LOTS === game ? 'todayLotTaskCompleteTimes' : 'todayBlessTaskCompleteTimes'
    const rewardKey = GAME_TYPE.DRAW_LOTS === game ? 'drawLotsTimes' : 'sendBlessingTimes'
    const sellerIdKey = GAME_TYPE.DRAW_LOTS === game ? 'lotFollowSellerIds' : 'blessFollowSellerIds'
    const reward = type === 'shareForHelp' ? times % INVITE_USER_TIME[game] === 0 ? 1 : 0 : 1
    const projection = {
      $inc: {
        [rewardKey]: reward,
        [key]: reward,
      },
      $set: {
        updateTime: Date.now()
      }
    } as any
    if (type === 'favorShop' && !!sellerId) {
      projection.$push = { [sellerIdKey]: sellerId }
    }
    return await this.updateUser(user._id, projection)
  }
}
